﻿// <copyright file="Entity.cs" company="City of Portland, BES-ASM">
// </copyright>
// <summary>Entity abstract class</summary>

namespace BESASM.EMGAATS.BusinessClasses
{
  using System;
  using System.Collections.Generic;
  using System.ComponentModel;
  using System.Linq;
  using System.Runtime.CompilerServices;
  using System.Text;
  using System.Threading.Tasks;

  /// <summary>
  /// Entity abstract base class
  /// </summary>
  public abstract class Entity : IEntity
  {
    private int id;
    private EntityType entityType;
    private bool isModified;
    private INetwork internalNetwork;
    private string editedBy;
    private string editReason;
    private DateTime editDate;
    private DateTime appendDate;

    /// <summary>
    /// Initializes a new instance of the <see cref="Entity" /> class.
    /// </summary>
    /// <param name="id">The ID of the new Entity</param>
    public Entity(int id)
      : this()
    {
      this.id = id;
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="Entity" /> class.
    /// </summary>
    /// <param name="entityType">The type of the Entity</param>
    /// <param name="id">The ID of the Entity</param>
    public Entity(EntityType entityType, int id)
    {
      this.entityType = entityType;
      this.id = id;
    }

    private Entity()
    {
    }

    /// <summary>
    /// Raised when a property is changed on this entity.
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// Gets or sets the ID of the entity
    /// </summary>
    public int Id
    {
      get
      {
        return this.id; 
      }

      set
      {
        if (value != this.id)
        {
          Network network = this.Network as Network;
          if (network != null)
          {
            network.UpdateEntityKey(this, value);
          }

          this.id = value;
          this.NotifyPropertyChanged();
        }
      }
    }

    /// <summary>
    /// Gets the entity type for this entity.
    /// </summary>
    public EntityType EntityType
    {
      get { return this.entityType; }
    }

    /// <summary>
    /// Gets or sets the network this entity belongs to.
    /// </summary>
    public INetwork Network
    {
      get
      {
        return this.internalNetwork;
      }

      set
      {
        if (value != this.internalNetwork)
        {
          this.internalNetwork = value;
          this.NotifyPropertyChanged();
        }
      }
    }

    #region IAuditable Members

    /// <summary>
    /// Gets or sets the user who last edited this item
    /// </summary>
    public string EditedBy
    {
      get
      {
        return this.editedBy;
      }

      set
      {
        if (value != this.editedBy)
        {
          this.editedBy = value;
          this.NotifyPropertyChanged();
        }
      }
    }

    /// <summary>
    /// Gets or sets the reason for the last edit
    /// </summary>
    public string EditReason
    {
      get
      {
        return this.editReason;
      }

      set
      {
        if (value != this.editReason)
        {
          this.editReason = value;
          this.NotifyPropertyChanged();
        }
      }
    }

    /// <summary>
    /// Gets or sets the date and time that this item was created
    /// </summary>
    public DateTime AppendDate
    {
      get
      {
        return this.appendDate;
      }

      set
      {
        if (value != this.appendDate)
        {
          this.appendDate = value;
          this.NotifyPropertyChanged();
        }
      }
    }

    /// <summary>
    /// Gets or sets the date and time that this item was last edited
    /// </summary>
    public DateTime EditDate
    {
      get
      {
        return this.editDate;
      }

      set
      {
        if (value != this.editDate)
        {
          this.editDate = value;
          this.NotifyPropertyChanged();
        }
      }
    }

    #endregion

    /// <summary>
    /// Gets a value indicating whether this entity has been modified.
    /// </summary>
    public bool IsModified
    {
      get { return this.isModified; }
    }

    /// <summary>
    /// Sets the modified state of this entity.
    /// </summary>
    /// <param name="modified">A value indicating whether this entity is modified or not.</param>
    public void SetModified(bool modified)
    {
      this.isModified = modified;
    }

    /// <summary>
    /// Creates a new entity that is a copy of the current instance.
    /// </summary>
    /// <returns>A new object that is a shallow copy of this instance.</returns>
    public abstract object Clone();

    /// <summary>
    /// Gets the hash code equivalent for the Entity
    /// </summary>
    /// <returns>The hash code</returns>
    public override int GetHashCode()
    {
      return this.id.GetHashCode();
    }

    /// <summary>
    /// Determines whether this Entity is the same as another
    /// </summary>
    /// <param name="obj">The object being compared against</param>
    /// <returns>True if the Entity is the same as the object compared against</returns>
    public override bool Equals(object obj)
    {
      IEntity other = obj as IEntity;
      if (other != null && this.EntityType.Equals(other.EntityType))
      {
        return this.Id.Equals(other.Id);
      }
      else
      {
        return false;
      }
    }

    /// <summary>
    /// Fires an event indicating a property has changed
    /// </summary>
    /// <param name="propertyName">The name of the property</param>
    protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
    {
      this.SetModified(true);
      if (this.PropertyChanged != null)
      {
        this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
      }
    }

    /// <summary>
    /// Clones the audit properties for this entity
    /// </summary>
    /// <param name="clone">The cloned entity to copy the audit properties to</param>
    protected void CloneAuditProperties(IEntity clone)
    {
      clone.AppendDate = this.appendDate;
      clone.EditDate = this.editDate;
      clone.EditedBy = this.editedBy;
      clone.EditReason = this.editReason;
    }
  }
}
